package falcon.script;

import java.util.ArrayList;
import java.util.List;

import falcon.common.Errno;

public final class FalconLangLexicalAnalyzer {
	
	public FalconLangLexicalAnalyzer() {
		reset();
	}
	
	public void reset() {
		m_row = -1;
	}

	public int analyze(String line, ArrayList<FalconLangToken> tokenList) {
		m_line = line;
		m_column = 0;
		m_errno = Errno.SUCCESS;
		m_row ++;
		Status status = Status.Idle;
		while (true) {
			m_ch = '\0';
			if (m_column < m_line.length()) {
				m_ch = m_line.charAt(m_column);
			}
			if (status == Status.Idle) {
				status = processIdle(tokenList);
			} else if (status == Status.String) {
				status = processString(tokenList);
			} else if (status == Status.Arg) {
				status = processArg(tokenList);
			} else if (status == Status.Ident) {
				status = processIdent(tokenList);
			} else if (status == Status.Symbol) {
				status = processSymbol(tokenList);
			} else {
				status = Status.Error;
				m_errno = Errno.SCRIPT_LEXICAL_ERROR;
			}
			if (status == Status.End || status == Status.Error) {
				break;
			}
		}
		return m_errno;
	}
	
	private Status processIdle(List<FalconLangToken> tokenList) {
		char ch = m_ch;
		int row = m_row;
		int x = m_column;
		m_column ++;
		if (ch == '#' || ch == '\0') {
			return Status.End;
		} else if (isBlank(ch)) {
			return Status.Idle;
		} else if (ch == '"') {
			FalconLangToken token = new FalconLangToken(FalconLangToken.STRING, row, x + 1);
			tokenList.add(token);
			return Status.String;
		} else if (ch == '$') {
			FalconLangToken token = new FalconLangToken(FalconLangToken.ARG, row, x);
			tokenList.add(token);
			return Status.Arg;
		} else if (isLetter(ch)) {
			FalconLangToken token = new FalconLangToken(FalconLangToken.IDENT, row, x);
			tokenList.add(token);
			return Status.Ident;
		} else if (FalconLangToken.isSymbol(ch)) {
			FalconLangToken token = new FalconLangToken(FalconLangToken.SYMBOL, row, x);
			tokenList.add(token);
			return Status.Symbol;
		}
		m_errno = Errno.SCRIPT_LEXICAL_ERROR;
		return Status.Error;
	}
	
	private Status processString(List<FalconLangToken> tokenList) {
		char ch = m_ch;
		if (ch == '"') {
			int x = m_column;
			String line = m_line;
			char pre = '\0';
			if (x > 0 && x - 1 < line.length()) {
				pre = line.charAt(x - 1);
			}
			// 检查是否转义 \" 
			if (pre != '\\') {
				FalconLangToken token = tokenList.get(tokenList.size() - 1);
				token.subLine(line, x);
				m_column ++;
				return Status.Idle;
			}
		}
		if (ch != '\0') {
			m_column ++;
			return Status.String;
		}
		m_errno = Errno.SCRIPT_LEXICAL_ERROR;
		return Status.Error;
	}
	
	private Status processArg(List<FalconLangToken> tokenList) {
		char ch = m_ch;
		if (isDigital(ch)) {
			m_column ++;
			return Status.Arg;
		}
		int x = m_column;
		String line = m_line;
		FalconLangToken token = tokenList.get(tokenList.size() - 1);
		token.subLine(line, x);
		return Status.Idle;
	}
	
	private Status processIdent(List<FalconLangToken> tokenList) {
		char ch = m_ch;
		if (ch == '_' || isLetter(ch)) {
			m_column ++;
			return Status.Ident;
		}
		int x = m_column;
		String line = m_line;
		FalconLangToken token = tokenList.get(tokenList.size() - 1);
		token.subLine(line, x);
		if (FalconLangToken.isKeyword(token)) {
			token.type = FalconLangToken.KEYWORD;
		}
		return Status.Idle;
	}
	
	private Status processSymbol(List<FalconLangToken> tokenList) {
		char ch = m_ch;
		if (FalconLangToken.isSymbol(ch)) {
			m_column ++;
			return Status.Symbol;
		}
		int x = m_column;
		String line = m_line;
		FalconLangToken token = tokenList.get(tokenList.size() - 1);
		token.subLine(line, x);
		if (FalconLangToken.isOperator(token)) {
			token.type = FalconLangToken.OPERATOR;
		// 除了操作符以外的符号只能是单字符
		} else if (token.value.length() > 1) {
			m_errno = Errno.SCRIPT_LEXICAL_ERROR;
			return Status.Error;
		}
		return Status.Idle;
	}

	private static boolean isBlank(char ch) {
		return ch == ' ' || ch == '\t';
	}
	
	private static boolean isLetter(char ch) {
		return ch >= 'a' && ch <= 'z';
	}
	
	private static boolean isDigital(char ch) {
		return ch >= '0' && ch <= '9';
	}
	
	private enum Status {
		Idle,
		String,
		Arg,
		Ident,
		Symbol,
		End,
		Error,
	}
	
	private int m_errno;
	private char m_ch;
	private int m_row;
	private int m_column;
	private String m_line;
}
